package com.cyy.view.tools

import com.cyy.app.Styles
import javafx.geometry.Pos
import javafx.scene.control.ListView
import javafx.scene.layout.HBox
import javafx.scene.paint.Color
import tornadofx.*
import java.io.BufferedReader
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import kotlin.random.Random

class ReadFilesRequest(val file: File) : FXEvent(EventBus.RunOn.BackgroundThread)

class Cv1 : Fragment() {
    var pos = BoardPositions.positions
    private val player = Player(
            id = 1,
            username = "Player 1",
            position = 1,
            money = Money()
    )

    override val root = pane {
        imageview("opoly/monopoly_board.jpg")

        pos.forEach {
            circle(it.centerX, it.centerY, 5.0) {
                fill = Color.RED
            }
        }

        imageview(player.icon).apply {
            x = pos[player.position - 1].centerX - 11.0
            y = pos[player.position - 1].centerY - 11.0
        }

        prefWidth = 732.0
        prefHeight = 732.0

        // roll dice and move animation
        val diceRoll = rollDice()
        animatePlayer(diceRoll)

    }.addClass(Styles.gameboard)

    private fun rollDice(): Int = (1..12).random()

    private fun animatePlayer(spaces: Int) {
        val originalPosition = player.position
        var newPosition = 0

        if (player.position + spaces > 40) {
            newPosition = spaces - (40 - originalPosition)
            // collect 200
            player.money.m100 += 2
        } else {
            newPosition = player.position + spaces
        }

        player.position = newPosition

        // animate piece
        when {
            originalPosition in 1..11 &&  newPosition in 1..11 -> TODO() // move up
            originalPosition in 11..21 &&  newPosition in 11..21 -> TODO() // move right
            originalPosition in 21..31 &&  newPosition in 21..31 -> TODO() // move down
            originalPosition in 31..40 &&  (newPosition in 31..40 || newPosition == 1) -> TODO() // move left
            originalPosition in 1 until 11 &&  newPosition in 12 until 21 -> TODO() // move up then right
            originalPosition in 11 until 21 &&  newPosition in 22 until 31 -> TODO() // move left then down
            originalPosition in 21 until 31 &&  newPosition in 32 until 41 -> TODO() // move down then right
            originalPosition in 31 until 40 &&  newPosition in 2 until 11 -> TODO() // move right then up
        }
    }

    private fun ClosedRange<Int>.random() = Random.nextInt(endInclusive - start) + start
}

object BoardPositions {
    private val positionsById = BoardPositions::class.java.getResource("opoly/positions.csv").readText().lines()
            .asSequence()
            .map { it.split(",") }
            .map { Position(it[0].toInt(), it[1].toDouble(), it[2].toDouble()) }
            .map { it.position to it }
            .toMap()

    val positions = positionsById.values.toList()
}

data class Player(val id: Int,
                  val username: String,
                  var position: Int = 0,
                  var money: Money,
                  var ownedProperties: List<Properties> = listOf(),
                  val icon: String = "opoly/player.png")

class Money(var m1: Int = 5,
            val m5: Int = 1,
            val m20: Int = 2,
            val m50: Int = 1,
            var m100: Int = 4,
            var m500: Int = 2) {
    fun get5sAmount(): Int = m5 * 5
    fun get20sAmount(): Int = m20 * 20
    fun get50sAmount(): Int = m50 * 50
    fun get100sAmount(): Int = m100 * 100
    fun get500sAmount(): Int = m500 * 500

    fun getTotal(): Int = m1 + get5sAmount() + get20sAmount() + get50sAmount() + get100sAmount() + get500sAmount()
}

data class Properties(val propertyName: String,
                      val propertyValue: Int,
                      var redHouses: List<RedHouse> = listOf(),
                      var greenHouses: List<GreenHouse> = listOf())

data class RedHouse(val taxValue: Int)
data class GreenHouse(val taxValue: Int)

data class Position(val position: Int, val centerX: Double, val centerY: Double)

class UploadFrag : Fragment("Upload") {
    val controller: MainViewController by inject()

    val consolePath = System.getProperty("os.name") + " ~ " + System.getProperty("user.name") + ": "
    lateinit var console: ListView<String>
    lateinit var overlay: HBox

    init {
        subscribe<ReadFilesRequest> { event ->
            controller.walk(event.file.absolutePath)
        }
    }
    override val root = stackpane {
        vbox {
            prefWidth = 800.0
            prefHeight = 600.0

            hbox {
                hboxConstraints {
                    marginLeftRight(20.0)
                    marginTopBottom(20.0)
                }
            }.addClass(Styles.top)

            stackpane {
                vboxConstraints {
                    marginTopBottom(40.0)
                    marginLeftRight(40.0)
                }
                vbox {
                    label("Fetching")
                    progressindicator()
                    alignment = Pos.TOP_CENTER
                    spacing = 4.0
                    paddingTop = 10.0
                }
                console = listview {
                    items.add(consolePath)
                    subscribe<PrintFileToConsole> { event ->
                        items.add(consolePath + event.file)
                        items.add(event.textFile)
                        items.add("===================================================================")
                    }
                }
            }

            button("Upload your project.") {
                action {
                    chooseDirectory {
                        title = "Choose a TornadoFX Project"
                        initialDirectory = File(System.getProperty("user.home"))
                    }?.let {
                        console.items.clear()
                        console.items.add("SEARCHING FILES...")
                        fire(ReadFilesRequest(it))
                    }
                }
                vboxConstraints {
                    marginLeft = 300.0
                    marginBottom = 40.0
                }
            }

        }.addClass(Styles.main)

        overlay = hbox {
            prefWidth = 800.0
            prefHeight = 600.0
            isMouseTransparent = true
        }.addClass(Styles.transparentLayer)
    }

}

class PrintFileToConsole(val file: String, val textFile: String): FXEvent()

class MainViewController: Controller() {

//    val view: UploadFrag by inject()

    /**
     * Open every file for AST parsing and send class breakdown for test generation
     */
    fun walk(path: String) {
        Files.walk(Paths.get(path)).use { allFiles ->
            allFiles.filter { path -> path.toString().endsWith(".kt") }
                    .forEach {path ->
                        val file = File(path.toUri())
                        readFiles(file, path.toUri().path)
                    }
        }
    }

    /**
     * Read file and start analyzing file with AST parsing
     */
    private fun readFiles(file: File, path: String) {
        val fileText = file.bufferedReader().use(BufferedReader::readText)

        if (filterFiles(fileText)) {
            fire(PrintFileToConsole(file.toString(), fileText))
        }
    }

    /**
     * Filter Kotlin files that are not Test files or Styles
     */
    private fun filterFiles(fileText: String): Boolean {
        return !fileText.contains("ApplicationTest()")
                && !fileText.contains("src/test")
                && !fileText.contains("@Test")
                && !fileText.contains("class Styles")
    }
}
